//
//  JLAuthorizationManager.m
//  JLAuthorizationManager<https://github.com/123sunxiaolin/JLAuthorizationManager.git>
//
//  <Wechat Public:iOSDevSkills>
//  Created by Jacklin on 2017/3/27.
//  Copyright © 2017年 com.jack.lin. All rights reserved.
//
//  This source code is licensed under the MIT-style license found in the
//  LICENSE file in the root directory of this source tree.
//

#import "JLAuthorizationManager.h"
@import UIKit;
@import Photos;
@import AssetsLibrary;
@import CoreTelephony;
@import AVFoundation;
@import AddressBook;
@import Contacts;
@import MediaPlayer;
@import Intents;
@import UserNotifications;

static NSString *const JLPushNotificationAuthorizationKey = @"JLPushNotificationAuthorizationKey";
static NSString *const JLRequestNotificationsKey = @"JL_requestedNotifications";

@interface JLAuthorizationManager ()<CLLocationManagerDelegate>

@property (nonatomic, copy) JLGeneralAuthorizationCompletion mapAlwaysAuthorizedHandler;
@property (nonatomic, copy) JLGeneralAuthorizationCompletion mapAlwaysUnAuthorizedHandler;
@property (nonatomic, copy) JLGeneralAuthorizationCompletion mapWhenInUseAuthorizedHandler;
@property (nonatomic, copy) JLGeneralAuthorizationCompletion mapWhenInUseUnAuthorizedHandler;

/**
 地理位置管理对象
 */
@property (nonatomic, strong) CLLocationManager *locationManager;
@property (nonatomic, assign) BOOL isRequestMapAlways;
@end

@implementation JLAuthorizationManager

+ (JLAuthorizationManager *)defaultManager{
    static JLAuthorizationManager *authorizationManager = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        authorizationManager = [[JLAuthorizationManager alloc] init];
    });
    return authorizationManager;
}

- (instancetype)init{
    if (self = [super init]) {
        _isRequestMapAlways = NO;
    }
    return self;
}


- (void)JL_requestAuthorizationWithAuthorizationType:(JLAuthorizationType)authorizationType
                                   authorizedHandler:(JLGeneralAuthorizationCompletion)authorizedHandler
                                 unAuthorizedHandler:(JLGeneralAuthorizationCompletion)unAuthorizedHandler{
    switch (authorizationType) {
        case JLAuthorizationTypePhotoLibrary:
            [self p_requestPhotoLibraryAccessWithAuthorizedHandler:authorizedHandler
                                               unAuthorizedHandler:unAuthorizedHandler];
            break;
            
        case JLAuthorizationTypeCellularNetWork:
            [self p_requestNetworkAccessWithAuthorizedHandler:authorizedHandler
                                          unAuthorizedHandler:unAuthorizedHandler];
            break;
        
        case JLAuthorizationTypeCamera:
            [self p_requestCameraAccessWithAuthorizedHandler:authorizedHandler
                                         unAuthorizedHandler:unAuthorizedHandler];
            break;
            
        case JLAuthorizationTypeMicrophone:
            [self p_requestAudioAccessWithAuthorizedHandler:authorizedHandler
                                        unAuthorizedHandler:unAuthorizedHandler];
            break;
        case JLAuthorizationTypeAddressBook:
            [self p_requestAddressBookAccessWithAuthorizedHandler:authorizedHandler
                                        unAuthorizedHandler:unAuthorizedHandler];
            break;
        
        case JLAuthorizationTypeNotification:
            [self p_requestNotificationAccessWithAuthorizedHandler:authorizedHandler
                                               unAuthorizedHandler:unAuthorizedHandler];
            break;
        case JLAuthorizationTypeMapAlways:
            [self p_requestMapAlwaysAccessWithAuthorizedHandler:authorizedHandler
                                            unAuthorizedHandler:unAuthorizedHandler];
            break;
        case JLAuthorizationTypeMapWhenInUse:
            [self p_requestMapWhenInUseAccessWithAuthorizedHandler:authorizedHandler
                                               unAuthorizedHandler:unAuthorizedHandler];
            break;

            
        default:
            NSAssert(!1, @"该方法暂不提供");
            
            break;
    }
}

- (void)JL_requestAccountAuthorizationWithAuthorizationType:(JLAuthorizationType)authorizationType
                                                    options:(NSDictionary *)options
                                          authorizedHandler:(JLGeneralAuthorizationCompletion)authorizedHandler
                                        unAuthorizedHandler:(JLGeneralAuthorizationCompletion)unAuthorizedHandler
                                               errorHandler:(void(^)(NSError *error))errorHandler{
    switch (authorizationType) {

            
        default:
            break;
    }
}

#pragma mark - Photo Library
- (void)p_requestPhotoLibraryAccessWithAuthorizedHandler:(JLGeneralAuthorizationCompletion)authorizedHandler
                                     unAuthorizedHandler:(JLGeneralAuthorizationCompletion)unAuthorizedHandler{
    if (@available(iOS 8.0, *)) {
        //used `PHPhotoLibrary`
        PHAuthorizationStatus authStatus = [PHPhotoLibrary authorizationStatus];
        if (authStatus == PHAuthorizationStatusNotDetermined) {
            [PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus status) {
                if (status == PHAuthorizationStatusAuthorized) {
                    dispatch_async(dispatch_get_main_queue(), ^{
                        authorizedHandler ? authorizedHandler(): nil;
                    });
                }else{
                    dispatch_async(dispatch_get_main_queue(), ^{
                        unAuthorizedHandler ? unAuthorizedHandler(): nil;
                    });
                }
            }];
        }else if (authStatus == PHAuthorizationStatusAuthorized){
            authorizedHandler ? authorizedHandler(): nil;
        }else{
            unAuthorizedHandler ? unAuthorizedHandler(): nil;
        }
        
    }else{
        //used `AssetsLibrary`
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
        ALAuthorizationStatus authStatus = [ALAssetsLibrary authorizationStatus];
        if (authStatus == ALAuthorizationStatusAuthorized) {
            authorizedHandler ? authorizedHandler() : nil;
        }else{
            unAuthorizedHandler ? unAuthorizedHandler() : nil;
        }
#pragma clang diagnostic pop
    }
}

#pragma mark - Network
- (void)p_requestNetworkAccessWithAuthorizedHandler:(JLGeneralAuthorizationCompletion)authorizedHandler
                                unAuthorizedHandler:(JLGeneralAuthorizationCompletion)unAuthorizedHandler{
    
    if (@available(iOS 9.0, *)) {
        CTCellularData *cellularData = [[CTCellularData alloc] init];
        CTCellularDataRestrictedState authState = cellularData.restrictedState;
        if (authState == kCTCellularDataRestrictedStateUnknown) {
            cellularData.cellularDataRestrictionDidUpdateNotifier = ^(CTCellularDataRestrictedState state){
                if (state == kCTCellularDataNotRestricted) {
                    dispatch_async(dispatch_get_main_queue(), ^{
                        authorizedHandler ? authorizedHandler() : nil;
                    });
                }else{
                    dispatch_async(dispatch_get_main_queue(), ^{
                        unAuthorizedHandler ? unAuthorizedHandler() : nil;
                    });
                }
            };
        }else if (authState == kCTCellularDataNotRestricted){
            authorizedHandler ? authorizedHandler() : nil;
        }else{
            unAuthorizedHandler ? unAuthorizedHandler() : nil;
        }
    } else {
        // Fallback on earlier versions
    }
}

#pragma mark - AvcaptureMedia
- (void)p_requestCameraAccessWithAuthorizedHandler:(JLGeneralAuthorizationCompletion)authorizedHandler
                               unAuthorizedHandler:(JLGeneralAuthorizationCompletion)unAuthorizedHandler{
    
    AVAuthorizationStatus authStatus = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
    if (authStatus == AVAuthorizationStatusNotDetermined) {
        [AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted) {
            if (granted) {
                dispatch_async(dispatch_get_main_queue(), ^{
                    authorizedHandler ? authorizedHandler() : nil;
                });
            }else{
                dispatch_async(dispatch_get_main_queue(), ^{
                    unAuthorizedHandler ? unAuthorizedHandler() : nil;
                });
            }
        }];
        
    }else if(authStatus == AVAuthorizationStatusAuthorized){
        authorizedHandler ? authorizedHandler() : nil;
    }else{
        unAuthorizedHandler ? unAuthorizedHandler() : nil;
    }
}

- (void)p_requestAudioAccessWithAuthorizedHandler:(JLGeneralAuthorizationCompletion)authorizedHandler
                              unAuthorizedHandler:(JLGeneralAuthorizationCompletion)unAuthorizedHandler{
    
    AVAuthorizationStatus authStatus = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeAudio];
    if (authStatus == AVAuthorizationStatusNotDetermined) {
        [AVCaptureDevice requestAccessForMediaType:AVMediaTypeAudio completionHandler:^(BOOL granted) {
            if (granted) {
                dispatch_async(dispatch_get_main_queue(), ^{
                    authorizedHandler ? authorizedHandler() : nil;
                });
            }else{
                dispatch_async(dispatch_get_main_queue(), ^{
                    unAuthorizedHandler ? unAuthorizedHandler() : nil;
                });
            }
        }];
        
    }else if(authStatus == AVAuthorizationStatusAuthorized){
        authorizedHandler ? authorizedHandler() : nil;
    }else{
        unAuthorizedHandler ? unAuthorizedHandler() : nil;
    }
}

#pragma mark - AddressBook
- (void)p_requestAddressBookAccessWithAuthorizedHandler:(JLGeneralAuthorizationCompletion)authorizedHandler
                              unAuthorizedHandler:(JLGeneralAuthorizationCompletion)unAuthorizedHandler{
    if (@available(iOS 9.0, *)) {
        CNAuthorizationStatus authStatus = [CNContactStore authorizationStatusForEntityType:CNEntityTypeContacts];
        if (authStatus == CNAuthorizationStatusNotDetermined) {
            CNContactStore *contactStore = [[CNContactStore alloc] init];
            [contactStore requestAccessForEntityType:CNEntityTypeContacts completionHandler:^(BOOL granted, NSError * _Nullable error) {
                if (granted) {
                    dispatch_async(dispatch_get_main_queue(), ^{
                        authorizedHandler ? authorizedHandler() : nil;
                    });
                }else{
                    dispatch_async(dispatch_get_main_queue(), ^{
                        unAuthorizedHandler ? unAuthorizedHandler() : nil;
                    });
                }
            }];
        }else if (authStatus == CNAuthorizationStatusAuthorized){
            authorizedHandler ? authorizedHandler() : nil;
        }else{
            unAuthorizedHandler ? unAuthorizedHandler() : nil;
        }
    } else {
        //iOS9.0 eariler

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
        
        ABAuthorizationStatus authStatus = ABAddressBookGetAuthorizationStatus();
        if (authStatus == kABAuthorizationStatusNotDetermined) {
            ABAddressBookRef addressBook = ABAddressBookCreate();
            ABAddressBookRequestAccessWithCompletion(addressBook, ^(bool granted, CFErrorRef error) {
                if (granted) {
                    dispatch_async(dispatch_get_main_queue(), ^{
                        authorizedHandler ? authorizedHandler() : nil;
                    });
                }else{
                    dispatch_async(dispatch_get_main_queue(), ^{
                        unAuthorizedHandler ? unAuthorizedHandler() : nil;
                    });
                }
            });
            
            if (addressBook) {
                CFRelease(addressBook);
            }
           
        }else if (authStatus == kABAuthorizationStatusAuthorized){
            authorizedHandler ? authorizedHandler() : nil;
        }else{
            unAuthorizedHandler ? unAuthorizedHandler() : nil;
        }
#pragma clang diagnostic pop
        
    }
}

#pragma mark - Notifacations

- (void)p_authorizedStatusForPushNotificationsWithCompletion:(void (^)(JLAuthorizationStatus status))completion {
    
    [[UNUserNotificationCenter currentNotificationCenter] getNotificationSettingsWithCompletionHandler:^(UNNotificationSettings * _Nonnull settings) {
        
        JLAuthorizationStatus status = JLAuthorizationStatusNotDetermined;
        switch (settings.authorizationStatus) {
                case UNAuthorizationStatusNotDetermined:
                status = JLAuthorizationStatusNotDetermined;
                break;
                
                case UNAuthorizationStatusDenied:
                status = JLAuthorizationStatusUnAuthorized;
                break;
                
                case UNAuthorizationStatusAuthorized:
                case UNAuthorizationStatusProvisional:
                status = JLAuthorizationStatusAuthorized;
                break;
                
            default:
                break;
        }
        
        dispatch_async(dispatch_get_main_queue(), ^{
            if (completion) {
                completion(status);
            }
        });
        
    }];
}

- (JLAuthorizationStatus)p_authorizedStatusForPushNotifications {
    
    BOOL isAuthorized = [[NSUserDefaults standardUserDefaults] boolForKey:JLPushNotificationAuthorizationKey];
    if (!isAuthorized) {
        return JLAuthorizationStatusUnAuthorized;
    }
    
    if (@available(ios 8.0, *)) {
        
        if ([[UIApplication sharedApplication] isRegisteredForRemoteNotifications]) {
            return JLAuthorizationStatusAuthorized;
        } else {
            return JLAuthorizationStatusUnAuthorized;
        }
        
    } else {
        
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
        if ([[UIApplication sharedApplication] enabledRemoteNotificationTypes] == UIRemoteNotificationTypeNone) {
#pragma clang diagnostic pop
            
            return JLAuthorizationStatusUnAuthorized;
        } else {
            return JLAuthorizationStatusAuthorized;
        }
    }
}

- (void)p_requestNotificationAccessWithAuthorizedHandler:(JLGeneralAuthorizationCompletion)authorizedHandler
                                     unAuthorizedHandler:(JLGeneralAuthorizationCompletion)unAuthorizedHandler {

    if (@available(iOS 10.0, *)) {
        UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
        [center requestAuthorizationWithOptions:UNAuthorizationOptionBadge | UNAuthorizationOptionSound | UNAuthorizationOptionAlert completionHandler:^(BOOL granted, NSError * _Nullable error) {
            
            dispatch_async(dispatch_get_main_queue(), ^{
                if (granted) {
                    if (authorizedHandler) {
                        authorizedHandler();
                    }
                } else {
                    if (unAuthorizedHandler) {
                        unAuthorizedHandler();
                    }
                }
            });
        }];
    } else if (@available(iOS 8.0, *)) {
        
        UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes: UIUserNotificationTypeBadge | UIUserNotificationTypeSound | UIUserNotificationTypeAlert categories:nil];
        [[UIApplication sharedApplication] registerUserNotificationSettings:settings];
        
    } else {
        
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
        UIRemoteNotificationType type = UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound | UIRemoteNotificationTypeAlert;
        [[UIApplication sharedApplication] registerForRemoteNotificationTypes:type];
#pragma clang diagnostic pop
        
        [[UIApplication sharedApplication] registerForRemoteNotifications];
    }
    
}

#pragma mark - Map
- (void)p_requestMapAlwaysAccessWithAuthorizedHandler:(JLGeneralAuthorizationCompletion)authorizedHandler
                                  unAuthorizedHandler:(JLGeneralAuthorizationCompletion)unAuthorizedHandler{
    if (![CLLocationManager locationServicesEnabled]) {
        NSAssert([CLLocationManager locationServicesEnabled], @"Location service enabled failed");
        return;
    }
    if (!self.locationManager) {
        self.locationManager = [[CLLocationManager alloc] init];
        self.locationManager.delegate = self;
    }
    
    CLAuthorizationStatus authStatus = [CLLocationManager authorizationStatus];
    if (authStatus == kCLAuthorizationStatusNotDetermined) {
        
        self.mapAlwaysAuthorizedHandler = authorizedHandler;
        self.mapAlwaysUnAuthorizedHandler = unAuthorizedHandler;
        [self.locationManager requestAlwaysAuthorization];
        self.isRequestMapAlways = YES;
        
    }else if (authStatus == kCLAuthorizationStatusAuthorizedAlways){
        authorizedHandler ? authorizedHandler() : nil;
    }else{
        unAuthorizedHandler ? unAuthorizedHandler() : nil;
    }
}

- (void)p_requestMapWhenInUseAccessWithAuthorizedHandler:(JLGeneralAuthorizationCompletion)authorizedHandler
                                     unAuthorizedHandler:(JLGeneralAuthorizationCompletion)unAuthorizedHandler{
    if (![CLLocationManager locationServicesEnabled]) {
        NSAssert([CLLocationManager locationServicesEnabled], @"Location service enabled failed");
        return;
    }
    if (!self.locationManager) {
        self.locationManager = [[CLLocationManager alloc] init];
        self.locationManager.delegate = self;
    }
    CLAuthorizationStatus authStatus = [CLLocationManager authorizationStatus];
    if (authStatus == kCLAuthorizationStatusNotDetermined) {
        
        self.mapWhenInUseAuthorizedHandler = authorizedHandler;
        self.mapAlwaysUnAuthorizedHandler = unAuthorizedHandler;
        [self.locationManager requestWhenInUseAuthorization];
        self.isRequestMapAlways = NO;
        
    }else if (authStatus == kCLAuthorizationStatusAuthorizedWhenInUse){
        authorizedHandler ? authorizedHandler() : nil;
    }else{
        unAuthorizedHandler ? unAuthorizedHandler() : nil;
    }
}
#pragma mark - Apple Music
- (void)p_requestAppleMusicAccessWithAuthorizedHandler:(JLGeneralAuthorizationCompletion)authorizedHandler
                                   unAuthorizedHandler:(JLGeneralAuthorizationCompletion)unAuthorizedHandler{
    if (@available(iOS 9.3, *)) {
        MPMediaLibraryAuthorizationStatus authStatus = [MPMediaLibrary authorizationStatus];
        if (authStatus == MPMediaLibraryAuthorizationStatusNotDetermined) {
            [MPMediaLibrary requestAuthorization:^(MPMediaLibraryAuthorizationStatus status) {
                if (status == MPMediaLibraryAuthorizationStatusAuthorized) {
                    dispatch_async(dispatch_get_main_queue(), ^{
                        authorizedHandler ? authorizedHandler() : nil;
                    });
                }else{
                    dispatch_async(dispatch_get_main_queue(), ^{
                        unAuthorizedHandler ? unAuthorizedHandler() : nil;
                    });
                }
            }];
        }else if (authStatus == MPMediaLibraryAuthorizationStatusAuthorized){
            authorizedHandler ? authorizedHandler() : nil;
        }else{
            unAuthorizedHandler ? unAuthorizedHandler() : nil;
        }
    } else {
        // Fallback on earlier versions
    }
}

#pragma mark - CLLocationManagerDelegate
- (void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status {
    
    if (status == kCLAuthorizationStatusAuthorizedAlways) {
        self.mapAlwaysAuthorizedHandler ? self.mapAlwaysAuthorizedHandler() : nil;
    }else if (status == kCLAuthorizationStatusAuthorizedWhenInUse){
        self.mapWhenInUseAuthorizedHandler ? self.mapWhenInUseAuthorizedHandler() : nil;
    }else{
        if (self.isRequestMapAlways) {
            self.mapAlwaysUnAuthorizedHandler ? self.mapAlwaysUnAuthorizedHandler() : nil;
        }else{
             self.mapWhenInUseUnAuthorizedHandler ? self.mapWhenInUseUnAuthorizedHandler() : nil;
        }
    }
}

@end
